package cn.sample.security.json.filter;

import com.alibaba.fastjson2.JSONObject;
import kotlin.text.Charsets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;

/**
 * <h2>JSON 用户名密码登录认证过滤器</h2>
 *
 * <p>支持使用 JSON 格式进行用户和密码登录的过滤器</p>
 * <p>这一步的目的，是为了使系统可以支持接收通过 Json 发送登录信息的认证请求。</p>
 *
 * @author HKD
 */
@Slf4j
public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    // 登录接口
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST");

    public JsonAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("认证方法不支持！");
        }
        if (request.getContentType() != null && request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {

            String queryString = StreamUtils.copyToString(request.getInputStream(), Charset.forName(Charsets.UTF_8.name()));

            if(StringUtils.isBlank(queryString)){
                throw new AuthenticationServiceException("登录信息不能为空！");
            }
            JSONObject jsonObject = JSONObject.parseObject(queryString);

            /**
             * 看过我的 CSDN 文章的同学肯定会疑问为什么不使用自定义的认证 token。
             * https://blog.csdn.net/hkdwsw/article/details/125905508
             *
             * 因为用户名和密码认证比较简单，Security 已经提供了一个认证 token，所以这里我就不再去自定义一个认证 token，而是直接
             * 使用 Security 提供的认证对象。
             *
             * 使用的差异会体现在后续的认证提供者（UsernamePasswordAuthenticationProvider）里面。
             */
            // 校验参数
            isNull(jsonObject,"username","用户名不能为空。");
            isNull(jsonObject,"password","密码不能为空。");

            // 把获取到的用户名和密码封装成未认证的 Token
            UsernamePasswordAuthenticationToken noAuthenticatedToken = new UsernamePasswordAuthenticationToken(jsonObject.get("username"),jsonObject.get("password"));

            // 调用抽象父类的认证管理器进行认证处理
            return this.getAuthenticationManager().authenticate(noAuthenticatedToken);
        }else{
            throw new AuthenticationServiceException("登录异常！");
        }
    }

    public void isNull (JSONObject jsonObject, String key, String msg){
        if(!jsonObject.containsKey(key) || StringUtils.isBlank(jsonObject.getString(key))){
            throw new AuthenticationServiceException(msg);
        }
    }


}
